#!/usr/bin/env python
"""
Axe Interpreter

An interpreter for programs written in the Axe Parser language for computers.

Author: Michael Lee (<michael.lee.0x2a@gmail.com>)
License: GNU Lesser GPL
"""

## System Libraries ##
import os
import argparse
import csv

## 3rd Party Libraries ##
import pygame
#import pygame._view     # Hack needed for pyinstaller to work.

## My Libraries ##
import meta
from meta import ExitException


def get_resource(path):
    full_path = os.path.join(meta.curdir(), path)

    pairs = {}
    with open(full_path, 'r') as configfile:
        reader = csv.reader(configfile, delimiter=':')
        for line in reader:
            uncomment = lambda x: x[:x.find('#') if x.find('#') != -1 else None].strip()
            if line:
                head = uncomment(line[0])
                try:
                    tail = uncomment(line[1])
                except IndexError:
                    continue
                if head:
                    pairs[head] = tail
    return pairs


class CommandIO():
    def __init__(self):
        """Creates the command line parser."""
        self._create_parser()
        self._options = {}
        return

    def _create_parser(self):
        """Adds options for the parser."""
        ## Creating the parser
        self._parser = argparse.ArgumentParser(description=meta.about,
                                               add_help=True,
                                               prog=meta.prog_name,
                                               formatter_class=argparse.RawTextHelpFormatter)
        ## Positional (mandatory) arguments
        self._parser.add_argument("input_path",
                                  nargs='?',
                                  type=str,
                                  help="The path to the file to execute.")
        ## Optional arguments
        self._parser.add_argument("-v", "--version",
                                  action="version",
                                  version="Version:  " + meta.version+'\nReleased: '+meta.release,
                                  help="Display the version and release date.")
        self._parser.add_argument("-t", "--test",
                                  action="store",
                                  nargs='?',
                                  const=None,
                                  default=None,
                                  type=str,
                                  choices=['lex','parse','interpret','io'],
                                  help="Tests specific components of this program.",
                                  dest="test")
        self._parser.add_argument("-i", "--interpret", "--interpreter",
                                  action="store_const",
                                  const=True,
                                  default=False,
                                  help="If this flag is included, the interpreter " \
                                       "will be run.",
                                  dest="interpret")
        return

    def parse(self):
        """Runs the command line parser.

        Arguments:
         - None

        Returns:
         - A dictionary of name-key pairs
        """
        options = self._parser.parse_args()
        
        if (not options.test) and (not options.interpret) and (not options.input_path):
            self._options["interpret"] = True
        else:
            self._options["input_path"] = options.input_path
            self._options["test"] = options.test
            self._options["interpret"] = options.interpret

        return self._options



class Emulator():
    def __init__(self, size=(96,64), pixel_size=3, caption="Axe Interpreter"):
        pygame.init()
        self.pixel_size = pixel_size
        self.size = self._actual_coords(size)
        
        self.screen = pygame.display.set_mode(self.size)
        pygame.display.set_caption(caption)
        self.background = pygame.Surface(self.size)
        
        self.buffer = pygame.Surface(self.size)
        self.backbuffer = pygame.Surface(self.size)
        
        self.rect((0, 0), self.size, (255,255,255), buffer_=self.background)
        self.rect((0, 0), self.size, (255,255,255), buffer_=self.buffer)
        self.rect((0, 0), self.size, (255,255,255), buffer_=self.backbuffer)
        
        self.clock = pygame.time.Clock()
        self.time = 0
        return 
        
    def update_screen(self, main=None, back=None, mode=2):
        #if main:
        #    p = pygame.PixelArray(self.buffer)
        #    p[:] = main
        if mode == 2:
            self.background = self.buffer
            self.screen.blit(self.background, (0,0))
            pygame.display.flip()
        return
    
    def kill_window(self):
        try:
            pygame.quit()
        except:
            pass    # Pygame not initialized?
        finally:
            raise ExitException
        return
    
    def rect(self, coordinates, dimensions, color=(0,0,0), buffer_=None):
        buffer_ = buffer_ if buffer_ else self.buffer
        coordinates = pygame.Rect(self._actual_coords(coordinates), self._actual_coords(dimensions))
        buffer_.fill(color, rect=coordinates)
        return

    def invert(self, coordinates, dimensions, buffer_=None):
        buffer_ = buffer_ if buffer_ else self.buffer
        (x, y) = self._actual_coords(coordinates)
        (w, h) = self._actual_coords(dimensions)
        w = x + w
        h = y + h
        
        buffer_sa = pygame.surfarray.array2d(buffer_)
        # Guarding for errors
        x = 0 if x < 0 else x
        y = 0 if y < 0 else y
        (max_w, max_h) = self.size
        w = max_w if w > max_w else w
        h = max_h if h > max_h else h
        for row in range(x, w):
            for col in range(y, h):
                a = buffer_sa[row][col]
                buffer_sa[row][col] = 0 if a else 256**3 - 1
        pygame.surfarray.blit_array(buffer_, buffer_sa)
        return
    
    def line(self, start, end, color=(0,0,0), buffer_=None):
        buffer_ = buffer_ if buffer_ else self.buffer
        start = self._actual_coords(start)
        end = self._actual_coords(end)
        pygame.draw.line(buffer_, color, start, end, self.pixel_size)
        return
    
    def get_pixel(self, x, y, buffer_=None):
        buffer_ = buffer_ if buffer_ else self.buffer
        try:
            out = buffer_.get_at(self._actual_coords((x,y)))
        except IndexError:
            return 0
        return 1 if out==(0,0,0,255) else 0
    
    def shift(self, x, y, buffer_=None):
        buffer_ = buffer_ if buffer_ else self.buffer
        x, y = self._actual_coords((x,y))
        buffer_.scroll(dx=x, dy=y)
        return
    
    def _actual_coords(self, pair):
        return tuple([x * self.pixel_size for x in list(pair)])
    
    def get_events(self):
        events = []
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                self.kill_window()
            else:
                events.append(event)
        return events
    
    def sanity_check(self):
        self.time += self.clock.tick()
        if self.time > 200: # This is an arbitrary number.
            if pygame.QUIT in pygame.event.get():
                self.kill_window
            self.time = 0
        return


def test_emulator():
    e = Emulator()
    i = 0
    while True:
        unhandled_events = e.get_events()
        
        i = (i + 1) % 2000
        if i < 1000:
            e.rect((20,20), (20,20))
        else:
            e.rect((20,20), (20,20), (255,255,255))
        e.update_screen()

if __name__ == '__main__':
    test_emulator()
            


    
    
